package org.roo.addon.compareto;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.itd.AbstractItdMetadataProvider;
import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.support.util.CollectionUtils;

/**
 * Provides {@link ComparetoMetadata}. This type is called by Roo to retrieve the metadata for this add-on.
 * Use this type to reference external types and services needed by the metadata type. Register metadata triggers and
 * dependencies here. Also define the unique add-on ITD identifier.
 * 
 * @since 1.1
 */
@Component
@Service
public final class ComparetoMetadataProvider extends AbstractItdMetadataProvider {

    /**
     * The activate method for this OSGi component, this will be called by the OSGi container upon bundle activation 
     * (result of the 'addon install' command) 
     * 
     * @param context the component context can be used to get access to the OSGi container (ie find out if certain bundles are active)
     */
    protected void activate(ComponentContext context) {
        metadataDependencyRegistry.registerDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType());
        addMetadataTrigger(new JavaType(RooCompareto.class.getName()));
    }
    
    /**
     * The deactivate method for this OSGi component, this will be called by the OSGi container upon bundle deactivation 
     * (result of the 'addon uninstall' command) 
     * 
     * @param context the component context can be used to get access to the OSGi container (ie find out if certain bundles are active)
     */
    protected void deactivate(ComponentContext context) {
        metadataDependencyRegistry.deregisterDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType());
        removeMetadataTrigger(new JavaType(RooCompareto.class.getName()));    
    }
    
    /**
     * Return an instance of the Metadata offered by this add-on
     */
    protected ItdTypeDetailsProvidingMetadataItem getMetadata(String metadataIdentificationString, 
    		JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, String itdFilename) {

    	final String[] excludeFields = {};
    	
    	final MemberDetails memberDetails = getMemberDetails(governorPhysicalTypeMetadata);
    	if(memberDetails == null){
    		return null;
    	}
    	
    	final JavaType javaType = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getName();
    	
    	final List<FieldMetadata> compareToFields = locateFields(javaType, excludeFields, memberDetails, 
    			metadataIdentificationString);
    	
    	
        return new ComparetoMetadata(metadataIdentificationString, aspectName, 
        		governorPhysicalTypeMetadata, compareToFields);
    }
    
    private List<FieldMetadata> locateFields(final JavaType javaType, final String[] excludeFields, 
    		final MemberDetails memberDetails, final String metadataIdentificationString){
    	
    	final SortedSet<FieldMetadata> locatedFields = new TreeSet<FieldMetadata>(new Comparator<FieldMetadata>(){
    		public int compare(final FieldMetadata l, final FieldMetadata r){
    			return l.getFieldName().compareTo(r.getFieldName());
    		}
    	});
    	
    	final List<?> excludeFieldList = CollectionUtils.arrayToList(excludeFields);
    	final FieldMetadata versionField = persistenceMemberLocator.getVersionField(javaType);
    	
    	for(final FieldMetadata field : memberDetails.getFields()){
    		if(excludeFieldList.contains(field.getFieldName().getSymbolName())){
    			continue;
    		}
    		if(Modifier.isStatic(field.getModifier()) || Modifier.isTransient(field.getModifier()) || 
    				field.getFieldType().isCommonCollectionType() || field.getFieldType().isArray()) {
    			continue;
    		}
    		if(versionField != null && field.getFieldName().equals(versionField.getFieldName())){
    			continue;
    		}
    		
    		locatedFields.add(field);
    		
    		metadataDependencyRegistry.registerDependency(field.getDeclaredByMetadataId(), metadataIdentificationString);
    	}
    	
    	return new ArrayList<FieldMetadata>(locatedFields);
    }
    
    /**
     * Define the unique ITD file name extension, here the resulting file name will be **_ROO_Compareto.aj
     */
    public String getItdUniquenessFilenameSuffix() {
        return "Compareto";
    }

    protected String getGovernorPhysicalTypeIdentifier(String metadataIdentificationString) {
        JavaType javaType = ComparetoMetadata.getJavaType(metadataIdentificationString);
        LogicalPath path = ComparetoMetadata.getPath(metadataIdentificationString);
        return PhysicalTypeIdentifier.createIdentifier(javaType, path);
    }
    
    protected String createLocalIdentifier(JavaType javaType, LogicalPath path) {
        return ComparetoMetadata.createIdentifier(javaType, path);
    }

    public String getProvidesType() {
        return ComparetoMetadata.getMetadataIdentiferType();
    }
}